package instructions;

import expressions.BadExpressionException;
import expressions.Expression;
import values.Function;
import values.Value;
import base.AST;
import base.Environment;
import base.Identifier;
import base.Interpreter;

public class FunctionCall implements Instruction {

	private Identifier name;
	private Identifier args[];
	private AST argsValues[];

	/**
	 * crate the function call name(args), where args take the values argsValues
	 * 
	 */
	public FunctionCall(Identifier name, Identifier args[], AST argsValues[]) {
		assert argsValues.length == 0 || argsValues[0] instanceof Expression
				|| argsValues[0] instanceof Value;

		this.name = name;
		this.args = args;
		this.argsValues = argsValues;
	}

	/**
	 * executes the function call by creating a new environment containing the
	 * values of the arguments of the function and the values in the contextual
	 * environment of the function
	 */
	public Environment instr(Environment env) throws BadInstructionException {

		assert env.getValue(this.name) instanceof Function;

		Function fun = (Function) env.getValue(this.name);

		Value actualArgs[] = null;
		
		if (this.argsValues != null) {
			actualArgs = new Value[this.argsValues.length];

			for (int i = 0; i < this.argsValues.length; i++) {

				try {
					actualArgs[i] = Interpreter.getBoolean(this.argsValues[i],
							env);
				} catch (BadExpressionException e1) {
					try {
						actualArgs[i] = Interpreter.getInt(this.argsValues[i],
								env);
					} catch (BadExpressionException e2) {
						try {
							actualArgs[i] = Interpreter.getArray(
									this.argsValues[i], env);
						} catch (BadExpressionException e3) {
							try {
								actualArgs[i] = Interpreter.getString(
										this.argsValues[i], env);
							} catch (BadExpressionException e4) {
								throw new BadInstructionException(
										"Trying to assign a non-boolean, non-integer, non-array or non-string to argument "
												+ this.args[i]
												+ " of function "
												+ this.name.getName());
							}
						}
					}
				}
			}
			
		}

		Value resultArgs[] = fun.execute(actualArgs);

		if (this.argsValues != null)
			for (int i = 0; i < this.argsValues.length; i++)
				if (this.args[i] != null)
					env.update(this.args[i], resultArgs[i]);

		return env;
	}

}
